<?php

class Agilet
{

	/**
	 * version code
	 */
	const VERSION = '1.0.0';

	/**
	 * Agilet is initialized ?
	 * @var bool
	 */
	private static $_initialized = FALSE;

	/**
	 * @var string
	 */
	private static $_appBasePath = NULL;

	/**
	 * @var string
	 */
	private static $_staticBaseUrl = NULL;

	/**
	 * @var string
	 */
	private static $_appDomain = NULL;

	/**
	 * @var user
	 */
	private static $_viewer = NULL;

	/**
	 * @var array
	 */
	private static $_modules = NULL;

	/**
	 * @var array
	 */
	private static $_plugins = array();

	/**
	 * register application variable
	 * @var array
	 */
	private static $_vars = array();

	/**
	 * get current request id to check data from log / profilling & etc when you want
	 * to inspector your app.
	 * it is helful when you deploy a large application.
	 */
	static public function getRequestId()
	{
		static $request_id;

		if (null == $request_id)
		{
			$request_id = Text::random(8);
		}
		return $request_id;
	}

	/**
	 * @return string
	 */
	static public function getAppBasePath()
	{
		return self::$_appBasePath;
	}

	/**
	 * @param string $basePath
	 * @return void
	 */
	static public function setAppBasePath($basePath)
	{
		self::$_appBasePath = $basePath;
	}

	/**
	 * @return string
	 */
	static public function getStaticBaseUrl()
	{
		return self::$_staticBaseUrl;
	}

	/**
	 * @param string $staticBaseUrl
	 * @return void
	 */
	static public function setStaticBaseUrl($staticBaseUrl)
	{
		self::$_staticBaseUrl = $staticBaseUrl;
	}

	/**
	 * call this method one only
	 */
	static public function __init()
	{
		if (self::$_initialized)
		{
			return;
		}

		self::$_initialized = true;

		//
		if (DEBUG)
		{
			ini_set('display_startup_errors', 1);
			ini_set('display_errors', 1);
		}

		if (APP_ENV == 'development')
		{
			ini_set('error_reporting', E_ALL);
		}

		self::$_appBasePath = BASE_URL;
		self::$_appDomain = APP_DOMAIN;
		self::$_staticBaseUrl = STATIC_BASE_URL;

		spl_autoload_register(array(
			'Agilet',
			'__autoload'
		));

		register_shutdown_function(array(
			'Agilet',
			'__shutdown'
		));

		set_error_handler(array(
			'Agilet',
			'__errorHandler'
		));

		set_exception_handler(array(
			'Agilet',
			'__exceptionHandler'
		));

		I18n::__init();

		if (!defined('AGILET_NO_SESSION') or !AGILET_NO_SESSION)
		{
			// bases session handler
			Session::__init();
		}

		self::$_modules = Config::readFile('module');
	}

	static public function getAppUrl()
	{
		return self::$_appUrl;
	}

	/**
	 * @param string $name
	 * @return object
	 */
	static public function getPlugin($name)
	{
		if (isset(self::$_plugins[$name]))
		{
			return self::$_plugins[$name];
		}

		$class = Agilet::getCanonicalClass($name, NULL);

		// try to load class
		Agilet::loadClass($class, true);

		// assign and create class object
		return self::$_plugins[$name] = new $class;
	}

	/**
	 * set autoload handler
	 * @link http://www.php.net/manual/en/function.spl-autoload-call.php
	 * @param string $class_anem
	 */
	static public function __autoload($class_name)
	{

		PROFILLING && ($token = Profiler::start('autoload', $class_name));

		$path = str_replace('_', '/', strtolower($class_name)) . '.php';

		if (file_exists($filename = SYSTEM_AGILET_PATH . '/class/' . $path))
		{
			include_once $filename;
			PROFILLING and Profiler::stop($token);
			return TRUE;
		}
		// load from external library
		$path = str_replace('_', '/', $class_name) . '.php';

		if (file_exists($filename = SYSTEM_LIBRARY_PATH . '/' . $path))
		{
			require_once $filename;
			PROFILLING and Profiler::stop($token);
			return TRUE;
		}
	}

	/**
	 * @param string $class_name
	 * @return bool
	 */
	static public function hasClass($class_name)
	{
		return class_exists($class_name) or self::__autoload($class_name);
	}

	/**
	 * @param string $class_name
	 * @param bool $throw_execption optional default false
	 * @return bool
	 * @throws Agilet_Exception
	 */
	static public function loadClass($class_name, $throw_exception = false)
	{
		if (!class_exists($class_name, false) or Agilet::__autoload($class_name))
		{
			if ($throw_exception)
			{
				throw new Agilet_Exception('class {class} does not exsits!', array('{class}' => $class));
			}
			return FALSE;
		}
		return TRUE;
	}

	/**
	 * get current phpfox version
	 * @return string
	 */
	static public function getVersion()
	{
		return self::VERSION;
	}

	/**
	 * @param     string $name
	 * @param     string $ext
	 * @return    string class name
	 */
	static public function getCanonicalClass($name, $type = null)
	{
		$name = str_replace('/', '.', $name);

		if (($pos = strpos($name, '.')) == false)
		{
			$name = $name . '.' . $name;
			$pos = strpos($name, '.');
		}

		$module = substr($name, 0, $pos);

		$sub = substr($name, $pos + 1);

		if ($type)
		{
			$sub = $type . '.' . $sub;
		}

		$file = $module . '/class/' . str_replace('.', '/', $sub) . '.php';

		if (file_exists($filename = APP_MODULE_PATH . '/' . $file))
		{
			require_once $filename;
		}
		else
		if (file_exists($filename = SYSTEM_MODULE_PATH . '/' . $file))
		{
			require_once $filename;
		}

		$class = ucfirst($module) . '_' . implode('_', array_map('ucfirst', explode('.', $sub)));

		return $class;
	}

	/**
	 * handle error
	 * @param  int     $errorno
	 * @param  string  $errstr
	 * @param  string  $errfile
	 * @param  int     $errline
	 * @param  handle  $errcontext
	 */
	static public function __errorHandler($errno, $errstr, $errfile = null, $errline = null, array $errcontext = null)
	{
		$level = Logger::INFO;
		$fatal = FALSE;

		$trace = Debug::getTrace();

		PROFILLING && ($token = Profiler::start('error', 'error handler'));

		switch ($errno)
		{
			case E_USER_NOTICE :
			case E_NOTICE :
				$level = Logger::NOTICE;
				break;

			case E_USER_WARNING :
			case E_WARNING :
			case E_COMPILE_WARNING :
			case E_CORE_WARNING :
			case E_DEPRECATED :
			case E_STRICT :
			case E_USER_DEPRECATED :
				$level = Logger::WARNING;
				break;

			case E_COMPILE_ERROR :
			case E_CORE_ERROR :
			case E_ERROR :
			case E_PARSE :
			case E_RECOVERABLE_ERROR :
			case E_USER_ERROR :
				$level = Logger::ERROR;
				$fatal = TRUE;
				break;
		}

		$message = sprintf('[%1$d] %2$s (%3$s) [%4$d]' . PHP_EOL . '%5$s', $errno, $errstr, Debug::getPath($errfile), $errline, $trace);

		Logger::instance() -> write($message, $level);

		PROFILLING && Profiler::stop($token);

		if ($fatal)
		{
			self::sendFatalError($trace);
		}
		return 1;
	}

	static public function sendFatalError($trace = '')
	{
		// Clean any previous output from buffer
		while (ob_get_level() > 0)
		{
			ob_end_clean();
		}

		echo View::render('core/html/error/fatal', $data = array('trace' => $trace));
		die(1);
	}

	/**
	 * handle exception
	 * @param Exception $exception
	 * @return true/false
	 */
	static public function __exceptionHandler($exception)
	{
		$message = Debug::getTrace() . PHP_EOL . $exception -> getMessage();
		Logger::instance() -> write($message, 'ERROR');
		return 1;
	}

	/**
	 * handle shutdown
	 */
	static public function __shutdown()
	{

		/**
		 * code here
		 */
		// Logger::instance() -> flush();

		if ($hooks = Hook::load('shutdown'))
		{
			Hook::emit($hooks, 'shutdown');
		}
	}

	/**
	 * get url
	 * @param string $path
	 * @param array|string $params optional default null
	 * @param string $baseUrl optional default null
	 * @return string
	 */
	static public function getUrl($path, $params = null, $baseUrl = null)
	{
		if (strpos($path, 'http') === 0)
		{
			return $path . ($params ? ('?' . http_build_query($params, null, '&')) : null);
		}

		if (null == $baseUrl)
		{
			$baseUrl = 'http://' . self::$_appDomain . self::$_appBasePath;
		}

		$path = $baseUrl . trim($path, '/');

		if (is_string($params))
		{
			return $path . '/' . $params;
		}
		else
		if (is_array($params))
		{
			return $path . '?' . http_build_query($params, null, '&');
		}

		return $path;
	}

	/**
	 * @return User_Model_User|Null
	 */
	static public function getViewer()
	{
		if (null == self::$_viewer)
		{
			$user = null;

			if (isset($_SESSION['uid']) && $id = intval($_SESSION['uid']))
			{
				$table = Model::getTable('user');
				$user = $table -> find($id) -> current();
			}
			if (null == $user)
			{
				$user = false;
			}
			self::$_viewer = $user;
		}
		return self::$_viewer;
	}

	/**
	 * check if module is enabled
	 * @param string $module_name
	 * @return bool
	 */
	static public function hasModule($module_name)
	{
		return isset(self::$_modules[$module_name]);
	}

	/**
	 * @param string $var_name
	 * @return any
	 */
	static public function getVar($var_name)
	{
		return isset(self::$_vars[$var_name]) ? self::$_vars[$var_name] : null;
	}

	/**
	 * <pre>
	 *   Agilet::setVar('breadcrum','somthinglikethis')
	 * </pre>
	 *
	 * @param string $var_name
	 * @param mixed $var_value
	 * @return void
	 */
	static public function setVar($var_name, $var_value = NULL)
	{
		if ($var_value === NULL && is_array($var_name))
		{
			foreach ($var_name as $key => $value)
			{
				self::$_vars[$key] = $value;
			}
		}
		else
		{
			self::$_vars[(string)$var_name] = $var_value;
		}
	}

}
